package commRedisService

import (
	"errors"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEntity"
	"gitee.com/fierce_wolf/go-fox-edge-common/commRedis"
	"strconv"
	"sync"
	"time"
)

type RedisService struct {
	mutex        sync.RWMutex                  // 读写锁
	EntityType   string                        // 实体类型
	dataMap      map[string]commEntity.IEntity // 数据表的记录数据：重量级数据
	agileMap     map[string]int64              // 数据表的敏捷状态：轻量级数据
	updateTime   int64                         // redis的最近刷新时间
	needSave     bool                          // 生产者：是否需要更新缓存到redis
	inited       bool                          // 是否已经完成初始化
	typeNotify   ConsumerTypeNotify            // 更新通知：类型级别
	entityNotify []ConsumerEntityNotify        // 更新通知：实体级别
}

// ConsumerTypeNotify Type级别
type ConsumerTypeNotify interface {
	Notify(updateTime int64, addMap map[string]commEntity.IEntity, delSet map[string]commEntity.IEntity, mdyMap map[string]commEntity.IEntity)
}

// ConsumerEntityNotify Entity级别
type ConsumerEntityNotify interface {
	getServiceKey() string                  // key
	notifyInsert(entity commEntity.IEntity) // 新增
	notifyUpdate(entity commEntity.IEntity) // 修改
	notifyDelete(serviceKey string)         // 删除
}

func (e *RedisService) loadAllEntities() error {
	if e.EntityType == "" {
		return errors.New("redisService的EntityType为空")
	}

	// 从redis中读取数据
	updateTime, err := readSyncTime(e.EntityType)
	if err != nil {
		return err
	}
	agileMap, err := readAllAgileMap(e.EntityType)
	if err != nil {
		return err
	}
	dataMap, err := readAllDataMap(e.EntityType)
	if err != nil {
		return err
	}

	// 保存数据
	e.setDataMap(dataMap)
	e.setAgileMap(agileMap)
	e.setUpdateTime(updateTime)

	return nil
}

func (e *RedisService) loadAgileEntities() error {
	if e.EntityType == "" {
		return errors.New("redisService的EntityType为空")
	}

	updateTime, err := readSyncTime(e.EntityType)
	if err != nil {
		return err
	}
	newUpdateTimes, err := readAllAgileMap(e.EntityType)
	if err != nil {
		return err
	}

	// 保存更新时间
	e.setUpdateTime(updateTime)

	// 比较敏捷数据，获得是否需要重新装载全局实体数据，如果要的话，涉及哪些差异数据
	diffList, addList, delList := e.compareLoadAgile(newUpdateTimes)

	// 获得是否需要重新装载全局实体数据
	if len(addList) > 0 || len(delList) > 0 {
		// 结构不同，全量刷新数据
		err := e.loadAllEntities()
		if err != nil {
			return err
		}
	} else {
		// 数据变化程度：
		if len(diffList)*10 > e.getAgileMapLen() || len(diffList) > 64 {
			// 变化幅度很大：1/10的数据发生变化，或者变化的数据超过10条，一刷新10次，还不如全部更新
			err := e.loadAllEntities()
			if err != nil {
				return err
			}
		} else {
			dataKey := RedisPrefix + ":" + e.EntityType + ":data"
			for k := range diffList {
				value, err := commRedis.Client.HGet(dataKey, k).Result()
				if err != nil {
					return err
				}

				entity, err := commEntity.BuildEntityByJson(e.EntityType, &value)
				if err != nil {
					return err
				}

				agile := newUpdateTimes[k]

				e.setAgileByKey(k, agile)
				e.setEntityByKey(k, entity)
			}
		}
	}

	// 类型级别的通知
	e.notifyType(addList, delList, diffList)

	// 实体级别的通知
	e.notifyEntity(addList, delList, diffList)

	return nil
}

func (e *RedisService) saveAllEntities() error {
	writeMap := e.buildAgileMapToJson()
	err := syncAllEntityMapToRedis(e.EntityType, "agile", writeMap)
	if err != nil {
		return err
	}

	// 保存敏捷数据
	writeMap = e.buildDataMapToJson()
	err = syncAllEntityMapToRedis(e.EntityType, "data", writeMap)
	if err != nil {
		return err
	}

	// 将时间戳保存到redis
	e.setUpdateTime(time.Now().UnixMilli())
	_, err = saveSyncTime(e.EntityType, e.getUpdateTime())
	if err != nil {
		return err
	}

	e.setNeedSave(false)

	return nil
}

func (e *RedisService) saveAgileEntities() error {
	syncKey := RedisPrefix + ":" + e.EntityType + ":sync"
	dataKey := RedisPrefix + ":" + e.EntityType + ":data"
	agileKey := RedisPrefix + ":" + e.EntityType + ":agile"

	updateTime := time.Now().UnixMilli()

	// 先读取redis里的记录状态
	oldUpdateTimes, err := readAllAgileMap(e.EntityType)
	if err != nil {
		return err
	}

	// 检查：记录结构是否不同（发生了增加和删除）
	addList, delList := e.differSaveAgile(oldUpdateTimes)

	if len(addList) != 0 || len(delList) != 0 {
		// 结构不同，全量刷新数据
		err := e.saveAllEntities()
		if err != nil {
			return err
		}
	} else {
		// 数据变化程度：
		diff := e.compareSaveAgile(oldUpdateTimes)
		if len(diff)*10 > e.getAgileMapLen() || len(diff) > 64 {
			// 变化幅度很大：1/10的数据发生变化，或者变化的数据超过64条，一刷新64次，还不如全部更新
			err := e.saveAllEntities()
			if err != nil {
				return err
			}
		} else {
			for k := range diff {
				// 将数据保存到redis
				jsn, err := e.getDataMapValue(k).BuildToJson()
				if err != nil {
					return err
				}

				commRedis.Client.HSet(dataKey, k, jsn)

				// 把敏捷数据也写入redis和缓存
				commRedis.Client.HSet(agileKey, k, updateTime)
				e.setAgileByKey(k, updateTime)
			}

			e.updateTime = updateTime
			commRedis.Client.Set(syncKey, strconv.FormatInt(updateTime, 10), -1)

			e.needSave = false
		}
	}

	return nil
}

func (e *RedisService) cleanAgileEntities() {
	syncKey := RedisPrefix + ":" + e.EntityType + ":sync"
	dataKey := RedisPrefix + ":" + e.EntityType + ":data"
	agileKey := RedisPrefix + ":" + e.EntityType + ":agile"

	commRedis.Client.HDel(syncKey)
	commRedis.Client.HDel(dataKey)
	commRedis.Client.HDel(agileKey)
}

func (e *RedisService) isNeedLoad() bool {
	updateTime, err := readSyncTime(e.EntityType)
	if err != nil {
		return false
	}

	return e.getUpdateTime() != updateTime
}
